package redis_pubsub

import (
	"context"
	"github.com/bytedance/sonic"
	"github.com/redis/go-redis/v9"
	redis_core "go_server/game_server/database/redis"
	"go_server/zinx/zconf"
	"go_server/zinx/zconstant"
	"go_server/zinx/zlog"
	"go_server/zinx/zutils"
	"strconv"
	"time"
)

type gameServerCache struct {
	serverMaps *zutils.ConcurrentHashMap[int]
	selfServer *zconstant.ServerInfo
	ps *redis.PubSub
}

var serverCache *gameServerCache

func StartPublish() {
	serverCache = &gameServerCache{
		serverMaps: zutils.NewConcurrentHashMap[int](zconstant.SERVER_CONCURRENT_SEG, zconstant.SERVER_CONCURRENT_TOTAL),
	}
	ctx := context.Background()
	publishStart(ctx)
	go startSubscribe(ctx)

	loadServers()
}

func loadServers() {
	subscribe_client := redis_core.GetSubscribeRedisClient()
	ctx := context.Background()
	serverMaps := subscribe_client.HGetAll(ctx, zconstant.KEY_SERVER).Val()
	var serverInfo zconstant.ServerInfo
	for _, serverStr := range serverMaps {
		if err := sonic.Unmarshal([]byte(serverStr), &serverInfo); err != nil {
			zlog.Panicf("加载服务器信息 [%s] 反序列化失败：[%v]", serverStr, err)
		}
		if serverInfo.ServerType == zconstant.SERVER_TYPE_GAME {
			continue
		}
		if !serverInfo.Running {
			continue
		}

		addServerCache(serverInfo)
	}

	zlog.Info("load server info from redis success!!!")
}

func publishStart(ctx context.Context) {
	serverInfo := &zconstant.ServerInfo{
		ServerId:   zconf.GlobalObject.Id,
		ServerType: zconstant.SERVER_TYPE_GAME,
		InnerIp:    zconf.GlobalObject.Inner,
		Port:       zconf.GlobalObject.WsPort,
		HttpPort:   zconf.GlobalObject.HttpPort,
		Running:    true,
	}

	serverCache.selfServer = serverInfo
	subscribe_client := redis_core.GetSubscribeRedisClient()
	marshal, err := sonic.Marshal(serverInfo)
	if err != nil {
		zlog.Panicf("服务器信息：%d,序列化失败！！！", serverInfo.ServerId)
	}
	cmd := subscribe_client.Publish(ctx, zconstant.GAME_SERVER_START, marshal)
	if cmd.Err() == nil {
		zlog.Infof("向频道[%s]发布服务器启动消息成功！", zconstant.GAME_SERVER_START)
	} else {
		zlog.Panicf("向频道[%s]发布服务器启动消息失败%v", zconstant.GAME_SERVER_START, cmd.Err())
	}

	err = subscribe_client.HSet(ctx, zconstant.KEY_SERVER, serverInfo.ServerId, marshal).Err()
	if err != nil {
		zlog.Panicf("向频道[%s]发布服务器启动消息失败%v", zconstant.GAME_SERVER_START, err)
	}
}

func PublishStop() {
	subscribe_client := redis_core.GetSubscribeRedisClient()
	ctx := context.Background()
	cmd := subscribe_client.Publish(ctx, zconstant.GAME_SERVER_STOP, serverCache.selfServer.ServerId)
	if cmd.Err() == nil {
		zlog.Infof("向频道[%s]发布服务器停止消息成功！", zconstant.GAME_SERVER_STOP)
	} else {
		zlog.Panicf("向频道[%s]发布服务器停止消息失败 %v", zconstant.GAME_SERVER_STOP, cmd.Err())
	}
	serverCache.selfServer.Running = false
	marshal, err := sonic.Marshal(serverCache.selfServer)
	if err != nil {
		zlog.Panicf("服务器信息：%d,序列化失败！！！", serverCache.selfServer.ServerId)
	}
	subscribe_client.HSet(ctx, zconstant.KEY_SERVER, serverCache.selfServer.ServerId, marshal)

	serverCache.ps.Close()
}

func startSubscribe(ctx context.Context) {
	channels := []string{zconstant.CENTER_SERVER_START, zconstant.CENTER_SERVER_STOP,
		zconstant.LOGIN_SERVER_START, zconstant.LOGIN_SERVER_STOP}
	subscribe_client := redis_core.GetSubscribeRedisClient()
	serverCache.ps = subscribe_client.Subscribe(ctx, channels...)

	for {
		msg, err := serverCache.ps.ReceiveMessage(ctx)
		if err != nil {
			zlog.Infof("start subscribe err !!! err is: %v", err)
			serverCache.ps.Close()
			serverCache.ps = nil
			break
		}

		channel := msg.Channel
		info := msg.Payload
		zlog.Infof("从[%s]频道接收到服务器订阅消息：[%s]", channel, info)
		switch channel {
		case zconstant.CENTER_SERVER_START, zconstant.LOGIN_SERVER_START:
			{
				var serverInfo zconstant.ServerInfo
				if err = sonic.Unmarshal([]byte(info), &serverInfo); err != nil {
					zlog.Panicf("订阅消息 [%s] 反序列化失败：[%v]", info, err)
				}
				addServerCache(serverInfo)
			}
		case zconstant.CENTER_SERVER_STOP, zconstant.LOGIN_SERVER_STOP:
			{
				serverId, err := strconv.Atoi(info)
				if err != nil {
					zlog.Panicf("订阅消息 [%s] 转类型失败：[%v]", info, err)
				}

				_, ok := serverCache.serverMaps.Get(serverId)
				if !ok {
					zlog.Errorf("找不到服务器[%d]的信息, err: %v", serverId, err)
					return
				}
				serverCache.serverMaps.Remove(serverId)

			}
		}
	}

	//todo 重连方案有待评估修改
	for {
		time.Sleep(time.Second)
		startSubscribe(ctx)
	}
}

func addServerCache(serverInfo zconstant.ServerInfo) {
	serverCache.serverMaps.Set(serverInfo.ServerId, serverInfo)
}
